#include "common.h"
#include "memory/cache.h"
#include "memory/memory.h"
#include "burst.h"
#include "cpu/reg.h"
#include "memory/TLB.h"
uint32_t dram_read(hwaddr_t, size_t);
void dram_write(hwaddr_t, size_t, uint32_t);

/* Memory accessing interfaces */
lnaddr_t seg_translate(swaddr_t addr,size_t len,uint8_t sreg)
{
	if(cpu.cr0.protect_enable==0)
	{
		return addr;//实模式
	}
	else{
		//越界
		if(addr + len < cpu.sr[sreg].cache.limit)
			{
				Assert(addr + len < cpu.sr[sreg].cache.limit, "Segmenttt Fault.");
			}
			
			lnaddr_t addr2=addr+cpu.sr[sreg].cache.base;
			
			return addr2;
	}
}//


void sregcache(uint8_t sreg)//
{
	segment_descriptor dess;//段描述符
	uint32_t gdtbase=cpu.gdtr.base;//gdt基址
	uint32_t off=cpu.sr[sreg].selector.index<<3;//偏移

	gdtbase=gdtbase+off;//gdt地址
	dess.des_first=lnaddr_read(gdtbase,4);
	dess.des_second=lnaddr_read(gdtbase+4,4);//根据索引找段描述符

	uint32_t base = (((uint32_t)dess.segment_base2) << 16) | dess.segment_base1 | (((uint32_t)dess.segment_base3) << 24);
	uint32_t limit = (((uint32_t)dess.segment_limit2) << 16) | dess.segment_limit1;

	if(dess.g!=0) 
		{
			limit = limit << 12;
		}
	
	cpu.sr[sreg].cache.base = base;
	cpu.sr[sreg].cache.limit = limit;//读入段的base和limit即可
}


hwaddr_t page_translate(lnaddr_t addr, size_t len) 
{
	if(cpu.cr0.protect_enable==0||cpu.cr0.paging==0)//不开启分页
	{
		return addr;
	}
	else{//分页
		hwaddr_t res;
		res=TLB_read(addr&0xfffff000);//只取高20位基址,先在TLB读页
		if(res!=-1)//TLB命中
		{
			res=res<<12;
			lnaddr_t temp_addr;
			temp_addr=addr&0xfff;//取信息位
			res=res+temp_addr;
			return res;
		}
		else{//TLB未命中，就乖乖根据table查找
			uint32_t diroff,pgoff,off;
			diroff=addr>>12;
			pgoff=diroff&(0x3ff);
			diroff=diroff>>10;
			off=addr&0xfff;
			hwaddr_t diraddr,pgaddr;
			diraddr=(cpu.cr3.page_directory_base<<12)+(diroff<<2);
			TBLENTRY dir;
			dir.val=hwaddr_read(diraddr,4);//
			/*if(dir.present!=0)
			{
				printf("INVALID PAGE. %x",addr);
				assert(0);
			}*/
			if(dir.present!=0)
			{
				Assert(dir.present!=0, "INVALID PAGE. %x", addr);
			}

			pgaddr=(dir.base<<12)+(pgoff<<2);//
			TBLENTRY pg;
			pg.val=hwaddr_read(pgaddr,4);
			/*if(pg.present!=0)//及时检查present位
			{
				printf("INVALID PAGE. %x",addr);
				assert(0);
			}*/
			if(pg.present!=0)
			{
				Assert(pg.present!=0, "INVALID PAGE. %x", addr);
			}

			TLB_write(addr&0xfffff000,pg.base);
			return off+(pg.base<<12);
		}
	}
}


uint32_t hwaddr_read(hwaddr_t addr, size_t len) {
	int cachenum=cache1_read(addr);

	uint8_t mask[BURST_LEN*2];

	uint32_t off=addr&(CACHEL1_BLMASK);

	if(off+len<=CACHEL1_BLSIZE){
		memcpy(mask,cache1[cachenum].data+off,len);
	}
	else{
		uint32_t lenpre=CACHEL1_BLSIZE-off;//未越界长度
		int re_cachenum=cache1_read(addr+lenpre);//读越界块
		
		memcpy(mask,cache1[cachenum].data+off,lenpre);//未越界
		memcpy(mask+lenpre,cache1[re_cachenum].data,len-lenpre);//已越界
	}
	uint32_t rel = unalign_rw(mask, 4) & (~0u >> ((4 - len) << 3));
	return rel;
}

void hwaddr_write(hwaddr_t addr, size_t len, uint32_t data) {
	cache1_write(addr, len, data);//
}

uint32_t lnaddr_read(lnaddr_t addr, size_t len) {
	assert(len == 1 || len == 2 || len == 4);
	hwaddr_t hwaddr = page_translate(addr, len); 
	return hwaddr_read(hwaddr, len);
}

void lnaddr_write(lnaddr_t addr, size_t len, uint32_t data) {
	assert(len == 1 || len == 2 || len == 4);
	hwaddr_t hwaddr = page_translate(addr, len); 
	hwaddr_write(hwaddr, len, data);
}

uint32_t swaddr_read(swaddr_t addr, size_t len,uint8_t sreg) {
#ifdef DEBUG
	assert(len == 1 || len == 2 || len == 4);
#endif
lnaddr_t lnaddr = seg_translate(addr,len,sreg);
	return lnaddr_read(lnaddr, len);
}

void swaddr_write(swaddr_t addr, size_t len, uint32_t data,uint8_t sreg) {
#ifdef DEBUG
	assert(len == 1 || len == 2 || len == 4);
#endif
lnaddr_t lnaddr = seg_translate(addr,len,sreg);
	lnaddr_write(lnaddr, len, data);
}

